﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Reflection;
using FinPlusCommon;
using System.Xml;
using System.IO;
using u = FinPlusUtility.Utility;
using log = Logger.Logger;

namespace FinPlusWPFControls
{
    public partial class FinPlusConfigMap : FinPlusControl
    {
        public override string[] States { get { return new string[] { "FilePath", "Height", "Width", "Left", "Top", "ConfigSelection", "ClientFilePath", "ServiceFilePath", "CompState" }; } }
        public override string[] TestStates { get { return new string[] { "TestString", "IsValid", "ClientFilePath", "ServiceFilePath" }; } }
        public override IFunctions Adaptor { get { return _funcs; } }
        public override string TestString { get { return Adaptor.ToString(); } }

        public string ClientFilePath { get { return _clientFilePath; } set { tClientFilePath.Text = _clientFilePath = value; } }
        public string ServiceFilePath { get { return _serviceFilePath; } set { tServiceFilePath.Text = _serviceFilePath = value; } }
        public string ConfigSelection { get { return _configSelection; } set { configSelection.SelectedValue = _configSelection = value; } }
        public string CompState { get { return GetCompState(); } set { SetCompState(value); } }

        private enum Params { FunctionName, Dock, Name, Id, Config, IsLink, ServiceType, ServiceName, Type, Component, ComponentMap, FinPlusConnectService, FinPlusService };
        private const string XmlNode = "root/current";
        private const string ClientComponentAssembly = "finpluscomponents";
        private IFunctions _comps;
        private IFunction _compParent;
        private readonly IFunctions _funcs;
        private readonly IDictionary<string, IFunction> _constCache, _methodCache, _propertyCache;
        private readonly IList<string> _docks;
        private string _clientFilePath, _serviceFilePath, _compPath, _configSelection;

        //construct
        public FinPlusConfigMap()
        {
            InitializeComponent();

            _constCache = new Dictionary<string, IFunction>();
            _methodCache = new Dictionary<string, IFunction>();
            _propertyCache = new Dictionary<string, IFunction>();
            _funcs = new Functions();
            _docks = new List<string>();
            IsValid = true;
        }

        //common control interface
        public override void CmdExecute(IFunction command)
        {
            try
            {
                CmdCapture(command);

                switch ((Cmds)Enum.Parse(typeof(Cmds), command.Name, true))
                {
                    case Cmds.Refresh: Refresh(); break;
                    case Cmds.Clear: Clear(); break;
                    case Cmds.Play: Play(); break;
                    case Cmds.ClientFile: ClientFileLoad(command["Tag"].ToString()); break;
                    case Cmds.ServiceFile: ServiceFileLoad(command["Tag"].ToString()); break;
                    case Cmds.Selection: ConfigSelect(); break;
                    default: throw new Exception(string.Format("event not recognised {0}", command.Name));
                }
            }
            catch (CmdExecuteException e)
            {
                IsValid = log.Log(Logger.Level.Warning, Id, Config, e.Message, e);
            }
        }

        public override void Dispose()  
        {
            if(_funcs != null)
                FinPlusDInject.Get.Dispose(_funcs);
        }

        //private
        private async void Refresh()
        {
            var wait = new FinPlusWait(compGrid, "Refreshing...");

            try
            {
                _comps = new Functions();
                var configNames = new List<string>();

                await Task.Run(() =>
                {
                    GetConfigs(_comps, configNames, _clientFilePath, "*.client");
                    GetConfigs(_comps, configNames, _serviceFilePath, "*.service");
                });

                ShowComps(_comps);

                configSelection.ItemsSource = configNames;
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Warning, Id, Config, e.Message, e);
            }

            wait.Dispose();
        }

        private async void Play()
        {
            var wait = new FinPlusWait(mapGrid, "Loading...");

            try
            {
                mapGrid.Children.Clear();
                _docks.Clear();
                var doc = new XmlDocument();

                await Task.Run(() => GetComponent(_comps, doc, null, _configSelection, _configSelection));
   
                mapGrid.Children.Add(new FinPlusMapDiagram() { Id = "CompMap", xmlDocument = doc });
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Warning, Id, Config, e.Message, e);
            }

            wait.Dispose();
        }

        private void Clear()
        {
            _comps = null;
            compGrid.Children.Clear();
            mapGrid.Children.Clear();
        }

        private void ShowComps(IFunctions funcs)
        {
            var funcDynamics = new FuncDynamics(funcs);
            var filterTray = new FinPlusFilterbarTray() { Id = "CompFilterbarTray" };
            var colTypes = u.StringToDictOfArrays("{{Exclude,{TimeStamp}},{ValueFunction,{Component}}}", ',', "{}");
            var colHelper = new FinPlusGridColHelper(null, colTypes, new string[]{"Component"}, null);
            var dataGrid = new FinPlusGroupGrid(new object[] { funcDynamics, filterTray }, colHelper, true, false) { Id = "CompGrid" };
    
            _compParent = new Function() { Object = dataGrid };
            SetCompState(_compPath);
            compGrid.Children.Clear();
            compGrid.Children.Add(dataGrid);
        }

        private void GetComponent(IFunctions funcs, XmlDocument doc, XmlNode parentNode, string name, string config = null)
        {
            if(config == null) config = name;

            var res = _comps.Values.Where(f => f[Params.Name.ToString()].Equals(name) && f[Params.Config.ToString()].Equals(config));
            if (res.Count() < 1) return;

            var func = res.First();

            var assembly = func[Params.Type.ToString()].Equals("client") ? Assembly.GetExecutingAssembly() : Assembly.LoadFile(u.GetAssemblyPath(Assembly.GetExecutingAssembly()) + ClientComponentAssembly + ".dll");
            var node = doc.CreateNode(XmlNodeType.Element, name, null);
            var typeName = func[Params.FunctionName.ToString()].ToString();
            node.Attributes.Append(u.XmlAttribute(doc, "TypeOf", string.Format("({0})", typeName)));
            node.Attributes.Append(u.XmlAttribute(doc, "Base", string.Format("({0})", FuncInfo.Type(assembly, typeName).BaseType.Name)));

            if (parentNode != null)
                parentNode.AppendChild(node);
            else
                doc.AppendChild(node);
            
            AddConst(doc, node, func, assembly);
            AddMethods(doc, node, func, assembly);
            AddPropertys(doc, node, func, assembly);
            AddDocks(doc, node, funcs, func, name, config);
        }

        private void AddConst(XmlDocument doc, XmlNode parentNode, IFunction func, Assembly assembly)
        {
            var funcName = func[Params.FunctionName.ToString()].ToString();
            IFunction cons;

            if (!_constCache.ContainsKey(funcName))
            {
                cons = FuncInfo.GetConst(func, assembly);
                _constCache.Add(funcName, cons);
            }
            else
            {
                cons = _constCache[funcName];
            }

            var node = doc.CreateNode(XmlNodeType.Element, "Const", null);
            var childNode = doc.CreateNode(XmlNodeType.Element, "List", null);

            foreach (var con in cons)
                if (con.Index > 3)
                    childNode.Attributes.Append(u.XmlAttribute(doc, con.Name, con.Value));

            node.AppendChild(childNode);
            parentNode.AppendChild(node);
        }

        private void AddMethods(XmlDocument doc, XmlNode parentNode, IFunction func, Assembly assembly)
        {
            var funcName = func[Params.FunctionName.ToString()].ToString();
            IFunction methods;

            if (!_methodCache.ContainsKey(funcName))
            {
                methods = FuncInfo.GetMethods(func, assembly);
                _methodCache.Add(funcName, methods);
            }
            else
            {
                methods = _methodCache[funcName];
            }
                
            var node = doc.CreateNode(XmlNodeType.Element, "Methods", null);
            var childNode = doc.CreateNode(XmlNodeType.Element, "List", null);

            foreach (var method in methods)
                if (method.Index > 3)
                    childNode.Attributes.Append(u.XmlAttribute(doc, method.Name, method.Value));
                
            node.AppendChild(childNode);
            parentNode.AppendChild(node);
        }

        private void AddPropertys(XmlDocument doc, XmlNode parentNode, IFunction func, Assembly assembly)
        {
            var funcName = func[Params.FunctionName.ToString()].ToString();
            IFunction propertys;

            if (!_propertyCache.ContainsKey(funcName))
            {
                propertys = FuncInfo.GetProperties(func, assembly);
                _propertyCache.Add(funcName, propertys);
            }
            else
                propertys = _propertyCache[funcName];
 
            var node = doc.CreateNode(XmlNodeType.Element, "Properties", null);
            var childNode = doc.CreateNode(XmlNodeType.Element, "List", null);
        
            foreach (var property in propertys)
                if (property.Index > 3)
                    childNode.Attributes.Append(u.XmlAttribute(doc, property.Name, property.Value));

            node.AppendChild(childNode);
            parentNode.AppendChild(node);
        }

        private void AddDocks(XmlDocument doc, XmlNode parentNode, IFunctions funcs, IFunction func, string name, string config)
        {
            var node = doc.CreateNode(XmlNodeType.Element, "Docks", null);
            parentNode.AppendChild(node);

            if (!string.IsNullOrEmpty(func[Params.ServiceType.ToString()].ToString()))
                 GetComponent(funcs, doc, node, func[Params.ServiceType.ToString()].ToString());
            else 
                foreach (var dock in Docks(func, name, config))
                    if (!HasDocked(config, name, dock))
                        GetComponent(funcs, doc, node, dock, config);
        }

        private bool HasDocked(string config, string name, string dock)
        {
            var check1 = string.Format("{0}|{1}|{2}", config, name, dock);
            var check2 = string.Format("{0}|{1}|{2}", config, dock, name);

            if( _docks.Contains(check1) || _docks.Contains(check2))
                return true;

            _docks.Add(check1);
            return false;
        }

        private IList<string> Docks(IFunction func, string name, string config)
        {
            var keys = new List<string>();
            var docks = func.Get(Params.Dock.ToString()).Value;

            if (string.IsNullOrEmpty(docks)) return keys;

            foreach (var link in docks.Split(';'))
                keys.Add(string.Format(link).Split(',').First().Split(':').First());

            return keys;
        }

        private void GetConfigs(IFunctions configs, IList<string> configNames, string folder, string extension)
        {
            var filePaths = Directory.GetFiles(folder, extension);
            foreach (var filePath in filePaths)
                GetConfig(configs, configNames, filePath, System.IO.Path.GetFileName(filePath));
        }

        private void GetConfig(IFunctions configs, IList<string> configNames, string filePath, string config)
        {
            var funcs = new Functions(filePath, XmlNode);
            var res = System.IO.Path.GetFileName(filePath).Split('.');
            configNames.Add(res[0]);

            foreach (var func in funcs.Values)
            {
                var comp = new Function(func[Params.FunctionName.ToString()].ToString(), Id, User, configs.NextKey());
                comp[Params.Name.ToString()] = func[Params.Id.ToString()];
                comp[Params.Dock.ToString()] = func[Params.Dock.ToString()];
                comp[Params.ServiceType.ToString()] = func[Params.ServiceType.ToString()];
                comp[Params.ServiceName.ToString()] = func[Params.ServiceName.ToString()];
                comp[Params.Config.ToString()] = res[0];
                comp[Params.Type.ToString()] = res[1].ToLower();
                comp[Params.Component.ToString()] = (Function)func;
                configs.Add(comp);
            }
        }

        private string GetCompState()
        {
            if(_compParent == null) return string.Empty;
            
            var path = u.GetAssemblyPath(Assembly.GetExecutingAssembly()) + "FinPlusViews\\CompState.State";
            FinPlusDInject.Get.ExtractStates(_compParent, Environment.UserName).ToXmlDoc(XmlNode).Save(path); ;
            return path;
        }

        private void SetCompState(string path)
        {
            if (!string.IsNullOrEmpty(path))
                FinPlusDInject.Get.InjectStates(_compParent, new Functions(_compPath = path, XmlNode));
        }

        private void ClientFileLoad(string path)
        {
            tClientFilePath.Text = _clientFilePath = path;
        }

        private void ServiceFileLoad(string path)
        {
            tServiceFilePath.Text = _serviceFilePath = path;
        }

        private void ConfigSelect()
        {
            _configSelection = configSelection.SelectedItem.ToString();
        }

        private void FolderRouter(string command)
        {
            var d = new FolderBrowserDialog();
            var res = d.ShowDialog();

            if (res == DialogResult.OK)
                CmdExecute(new Function(command, Id, User, -1, d.SelectedPath));
        }

        //cmds
        private enum Cmds { Refresh, Clear, Play, Selection, ClientFile, ServiceFile }

        private void Config_OnSelection(object s, RoutedEventArgs a) { CmdRouter(Cmds.Selection.ToString()); } 
    }
}





























































































//Copyright2013<jnrton>AllRightsReserved 
//Contact j.nrton@finpus